package com.onesports.intelligent.k12.polarlight.data.permission;

import com.onesports.intelligent.k12.polarlight.annotation.DataPermission;
import com.onesports.intelligent.k12.polarlight.common.errors.exceptions.UncheckedException;
import com.onesports.intelligent.k12.polarlight.constant.SystemCodeConstants;
import com.onesports.intelligent.k12.polarlight.constant.TableConstants;
import com.onesports.intelligent.k12.polarlight.domain.vo.auth.DataPermissionVO;
import com.onesports.intelligent.k12.polarlight.enums.DataPermissionJoinTypeEnum;
import com.onesports.intelligent.k12.polarlight.enums.DataPermissionTypeEnum;
import com.onesports.intelligent.k12.polarlight.security.SecurityUtils;
import jdk.nashorn.internal.objects.NativeJava;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.FromItem;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.util.cnfexpression.MultipleExpression;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;


/**
 * @author aj
 */
public class DataPermissionHandlerImpl implements DataPermissionHandler {

    @Override
    public Expression getSqlSegment(PlainSelect plainSelect, String mappedStatementId) {
        Expression where = plainSelect.getWhere();
        List<Join> joins = plainSelect.getJoins();
        DataPermission dataPermission = getAnnotation(mappedStatementId);
        if (dataPermission == null) {
            return where;
        }
        String columnName;
        List<String> orgIds = null;
        List<String> campusIds = null;
        String expressionAlias;
        Expression rightExpression = null;
        String tableName = dataPermission.tableName();
        String alias = dataPermission.alias();
        DataPermissionJoinTypeEnum joinType = dataPermission.joinType();
        DataPermissionTypeEnum dataPermissionType = dataPermission.value();
        DataPermissionVO dataPermissionVO = SecurityUtils.getDataPermission();
        String organizationId = SecurityUtils.getOrganizationId();
        boolean isSuper = SecurityUtils.isSuper();
        String systemCode = SecurityUtils.getSystemCode();
        Table fromItem = (Table) plainSelect.getFromItem();
        //是否外连接
        boolean isOuterJoin = DataPermissionJoinTypeEnum.OUTER_JOIN.equals(joinType);
        if (StringUtils.isEmpty(tableName)) {
            if (isOuterJoin && StringUtils.isEmpty(alias)) {
                throw new UncheckedException("tableName or alias must not be null");
            }
            tableName = fromItem.getName();
        }

        // 默认权限：运营端按机构，机构端按校区，超管拥有该层级所有权限
        if (DataPermissionTypeEnum.DEFAULT.equals(dataPermissionType)) {
            if (SystemCodeConstants.OPERATION.equals(systemCode)) {
                dataPermissionType = DataPermissionTypeEnum.BY_ORG;
            } else if (SystemCodeConstants.ORGANIZATION.equals(systemCode)) {
                dataPermissionType = DataPermissionTypeEnum.BY_CAMPUS;
            }
        }
        if (dataPermissionVO != null) {
            orgIds = dataPermissionVO.getOrgIds();
            campusIds = dataPermissionVO.getCampusIds();
        }

        if (StringUtils.isNotEmpty(alias)) {
            expressionAlias = alias;
        } else if (isOuterJoin) {
            expressionAlias = tableName;
        } else {
            Alias fromItemAlias = fromItem.getAlias();
            expressionAlias = fromItemAlias == null ? tableName : fromItemAlias.getName();
        }
        String orgColumnName = TableConstants.TABLE_ORGANIZATION.equals(tableName)
                ? TableConstants.COLUMN_ID : TableConstants.COLUMN_ORGANIZATION_ID;

        if (isSuper && SystemCodeConstants.ORGANIZATION.equals(systemCode)) {
            EqualsTo equalsTo = new EqualsTo();
            columnName = orgColumnName;
            equalsTo.setLeftExpression(buildColumn(expressionAlias, columnName));
            equalsTo.setRightExpression(new StringValue(organizationId));
            rightExpression = equalsTo;
        } else if (DataPermissionTypeEnum.BY_ORG.equals(dataPermissionType)) {
            columnName = orgColumnName;
            rightExpression = buildInWhere(expressionAlias, columnName, orgIds);
        } else if (DataPermissionTypeEnum.BY_CAMPUS.equals(dataPermissionType)) {
            columnName = TableConstants.TABLE_CAMPUS.equals(tableName)
                    ? TableConstants.COLUMN_ID : TableConstants.COLUMN_CAMPUS_ID;
            rightExpression = buildInWhere(expressionAlias, columnName, campusIds);
        }
        if (DataPermissionJoinTypeEnum.OUTER_JOIN.equals(joinType)) {
            if (CollectionUtils.isNotEmpty(joins)) {
                Iterator joinsIterator = joins.iterator();
                while (joinsIterator.hasNext()) {
                    Join joinItem = (Join) joinsIterator.next();
                    FromItem rightItem = joinItem.getRightItem();
                    if (rightItem instanceof Table) {
                        Table table = (Table) rightItem;
                        boolean isTargetTable = table.getName().equals(tableName)
                                || (table.getAlias() != null && table.getAlias().getName().equals(alias));
                        if (isTargetTable) {
                            if (CollectionUtils.isEmpty(joinItem.getOnExpressions())) {
                                throw new UncheckedException("not supported...");
                            }
                            EqualsTo equalsTo = new EqualsTo();
                            Expression onExpression = ((List<Expression>) joinItem.getOnExpressions()).get(0);
                            columnName = orgColumnName;
                            equalsTo.setLeftExpression(buildColumn(alias, columnName));
                            equalsTo.setRightExpression(new StringValue(organizationId));
                            rightExpression = new AndExpression(onExpression,
                                    rightExpression);
                            ((List<Expression>) joinItem.getOnExpressions()).set(0, rightExpression);
                        }
                    }
                }
            }
        } else {
            where = ObjectUtils.isNotEmpty(where) ?
                    new AndExpression(where, rightExpression) : rightExpression;
        }

        return where;
    }

    /**
     * 构建Column
     *
     * @param tableAlias 表别名
     * @param columnName 字段名称
     * @return 带表别名字段
     */
    public static Column buildColumn(String tableAlias, String columnName) {
        if (StringUtils.isNotEmpty(tableAlias)) {
            columnName = tableAlias + "." + columnName;
        }
        return new Column(columnName);
    }


    private DataPermission getAnnotation(String mappedStatementId) throws UncheckedException {
        final Class<?> clazz;
        try {
            clazz = Class.forName(mappedStatementId.substring(0, mappedStatementId.lastIndexOf(".")));
        } catch (ClassNotFoundException e) {
            throw new UncheckedException("check the code...");
        }
        String methodName = mappedStatementId.substring(mappedStatementId.lastIndexOf(".") + 1);
        boolean clazzAnnotationPresent = clazz.isAnnotationPresent(DataPermission.class);

        final Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                if (method.isAnnotationPresent(DataPermission.class)) {
                    return method.getAnnotation(DataPermission.class);
                }
                break;
            }
        }
        if (clazzAnnotationPresent) {
            return clazz.getAnnotation(DataPermission.class);
        }
        return null;
    }

    private Expression buildInWhere(String tableName, String columnName,
                                    List<String> permissionList) {
        InExpression inExpression = new InExpression();
        List<Expression> inList = new ArrayList<>();
        inExpression.setLeftExpression(buildColumn(tableName, columnName));
        if (CollectionUtils.isEmpty(permissionList)) {
            inList.add(new StringValue(""));
        } else {
            permissionList.forEach(id -> inList.add(new StringValue(id)));
        }

        MultipleExpression multipleExpression =
                new MultipleExpression(inList) {
                    @Override
                    public String getStringExpression() {
                        return ",";
                    }
                };
        inExpression.setRightExpression(multipleExpression);
        return inExpression;
    }

}
